﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Text;
using ParieurPro.DAL.DatabaseModel;
using ParieurPro.DAL.Exception;
using System.Data.SqlClient;

namespace ParieurPro.DAL
{
    /// <summary>
    /// Classe de gestion des événements.
    /// Un événement est une liste de matchs appartenant à une compétition (Contest). 
    /// Une compétition designant un championnat et une saison (ex : Ligue 1 saison 2011/12, Coupe de France 2011/12)
    /// Les parieurs ne parient pas sur un match mais sur un événement.
    /// Pour qu'un pari soit valide il faut parier sur tous les matchs de l'événement.
    /// Un événement a une date de début de pari (date à partir de laquelle l'événement devient l'évenement
    /// par défaut sur la page des paris). Il est toutefois possible de parier sur un événement même si la date d'ouverture
    /// n'est pas encore atteinte. 
    /// Un événement a une date de fin de pari, une fois cette date atteinte, tout pari devient impossible pour cet événement
    /// </summary>
    [Serializable()]
    public class EventObject
    {
        #region Attributes
        private int _eventId;
        private int _contestId;
        private int _dayNumber;
        private DateTime _openDate;
        private DateTime _closeDate;
        private string _displayName;
        private int _seasonId;
        private int _championshipId;
        private String _seasonName;
        #endregion

        #region Properties

        /// <summary>
        /// Identifiant unique de la journée de championnat (événement)
        /// Un événement représente un ensemble de matchs
        /// </summary>
        [Key()]
        [DataMemberAttribute()]
        public int EventId
        {
            get { return _eventId; }
            set { _eventId = value; }
        }

        /// <summary>
        /// Identifiant unique de la compétition à laquelle appartient
        /// l'événement (ex : Ligue 1 saison 2011-2012)
        /// </summary>
        [DataMemberAttribute()]
        public int ContestId
        {
            get { return _contestId; }
            set { _contestId = value; }
        }

        /// <summary>
        /// Identifiant unique du championnat auquel appartient
        /// l'événement (ex : Ligue 1)
        /// </summary>
        [DataMemberAttribute()]
        public int ChampionshipId
        {
            get { return _championshipId; }
            set { _championshipId = value; }
        }

        /// <summary>
        /// Numéro de la journée de championnat
        /// </summary>
        [DataMemberAttribute()]
        public int DayNumber
        {
            get { return _dayNumber; }
            set { _dayNumber = value; }
        }

        /// <summary>
        /// Date à partir de laquelle l'événement
        /// devient l'événement par défaut dans la page
        /// des paris
        /// </summary>
        [DataMemberAttribute()]
        public DateTime OpenDate
        {
            get { return _openDate; }
            set { _openDate = value; }
        }

        /// <summary>
        /// Date de fermeture des paris pour l'événement
        /// </summary>
        [DataMemberAttribute()]
        public DateTime CloseDate
        {
            get { return _closeDate; }
            set { _closeDate = value; }
        }

        /// <summary>
        /// Nom de la journée de championnat
        /// (ex : Journée n°12)
        /// </summary>
        [DataMemberAttribute()]
        public string DisplayName
        {
            get { return _displayName; }
            set { _displayName = value; }
        }

        /// <summary>
        /// Identifiant unique de la saison
        /// à laquelle appartient l'événement
        /// (ex 2010-2011)
        /// </summary>
        [DataMemberAttribute()]
        public int SeasonId
        {
            get { return _seasonId; }
            set { _seasonId = value; }
        }

        /// <summary>
        /// Nom à afficher de la saison
        /// </summary>
        [DataMemberAttribute()]
        public String SeasonName
        {
            get { return _seasonName; }
            set { _seasonName = value; }
        }

        #endregion

        #region Data access

        /// <summary>
        /// Chaîne représentant le nom de la journée de championnat
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return DisplayName;
        }

        /// <summary>
        /// Copie l'objet représentant la table "Events" de la base de données
        /// en objet "EventObject"
        /// </summary>
        /// <param name="dataToCopy">Objet à copier</param>
        /// <returns>Objet copié</returns>
        /// <exception cref="ArgumentException">Objet à copier vaut null</exception>
        public static EventObject CopyFromDatabaseObject(Events dataToCopy)
        {
            if (dataToCopy == null)
            {
                throw new ArgumentException("EventObject.CopyFromDatabaseObject - dataToCopy can not be null");
            }
            EventObject evt = new EventObject();
            evt.EventId = dataToCopy.EventId;
            evt.ChampionshipId = dataToCopy.Contests.ChampionshipId;
            evt.ContestId = dataToCopy.ContestId;
            evt.DayNumber = dataToCopy.DayNumber;
            evt.OpenDate = dataToCopy.OpenDate;
            evt.CloseDate = dataToCopy.CloseDate;
            evt.DisplayName = String.Empty;
            evt.SeasonId = dataToCopy.Contests.SeasonId;
            evt.SeasonName = dataToCopy.Contests.Seasons.Display;
            return evt;
        }

        /// <summary>
        /// Retourne le contenu d'une ligne de la table "Events" à partir de sa clé
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="eventId">Identifiant unique de l'événement</param>
        /// <returns>Objet "Events" contenu la ligne de la table "Events"</returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static Events GetEventFromDatabase(MyBetDBEntities dbContext, int eventId)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }
            var query = (from championshipday in dbContext.Events
                         where championshipday.EventId == eventId
                         select championshipday);
            Events days = query.FirstOrDefault();
            return days;
        }

        /// <summary>
        /// Retourne le contenu d'une ligne de la table "Events" à partir de l'identifiant du championnat,
        /// de l'identifiant de session et du numéro de journée
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="championshipId">Identifiant du championnat (ligue 1, ligue 2, ...)</param>
        /// <param name="seasonId">Identifiant de la saison (2011-12, 2012-13, ...)</param>
        /// <param name="dayNumber">Numéro de la journée</param>
        /// <returns>Contenu d'une ligne de la table "Events"</returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static Events GetEventFromDatabase(MyBetDBEntities dbContext, int championshipId, int seasonId, int dayNumber)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }
            // Extraction de l'objet Events depuis la base de données
            var query = (from championshipday in dbContext.Events
                         where championshipday.DayNumber == dayNumber && championshipday.Contests.ChampionshipId == championshipId && championshipday.Contests.SeasonId == seasonId
                         select championshipday);
            Events days = query.FirstOrDefault();
            return days;
        }

        /// <summary>
        /// Renvoie tous les événéments associés à une compétition personnalisée
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="customContestId">Identifiant unique de la compétition personnalisée</param>
        /// <returns>Liste des événements ou une liste vide si la compétition n'existe pas</returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static List<EventObject> GetEventsFromCustomContest(MyBetDBEntities dbContext, int customContestId)
        {
            List<EventObject> returnList = new List<EventObject>();
            StringBuilder sql = new StringBuilder();

            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }

            object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "customContestId", Value = customContestId }
                        };

            // Création de la requête SQL
            // Récupération de toutes les informations pour créer un objet EventObject 
            sql.Append("SELECT event.EventId, contest.ContestId,  contest.ChampionshipId, contest.SeasonId, event.DayNumber, event.OpenDate, event.CloseDate, season.Display as SeasonName ");
            sql.Append("FROM dbo.CustomContest as cc ");
            sql.Append("LEFT JOIN dbo.CustomContestLinks ccl ON ccl.CustomContestId = cc.CustomContestId ");
            sql.Append("LEFT JOIN dbo.Contests contest ON contest.ContestId = ccl.ContestId ");
            sql.Append("JOIN dbo.Events event ON event.ContestId = contest.ContestId ");
            sql.Append("LEFT JOIN dbo.Seasons season ON season.SeasonId = contest.SeasonId ");
            sql.Append("WHERE cc.CustomContestId=@customContestId ORDER BY event.CloseDate");
            // Exécution de la requete SQL
            IEnumerable<EventObject> results = dbContext.ExecuteStoreQuery<EventObject>(sql.ToString(), parameters);
            // Constitution de la liste
            if (results != null)
            {
                foreach (EventObject sqlObject in results)
                {
                    returnList.Add(sqlObject);
                }
            }

            return returnList;
        }

                /// <summary>
        /// Renvoie tous les événéments associés à un championnat et une saison
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="championshipId">Identifiant du championnat</param>
        /// <param name="seasonId">Identifiant de la saison</param>
        /// <returns>Liste des événements ou une liste vide si la compétition n'existe pas</returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static List<EventObject> GetEventsFromChampionshipId(MyBetDBEntities dbContext, int championshipId, int seasonId)
        {
            List<EventObject> returnList = new List<EventObject>();
            StringBuilder sql = new StringBuilder();

            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }

            object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "p1", Value = championshipId },
                           new SqlParameter { ParameterName = "p2", Value = seasonId }
                        };

            // Création de la requête SQL
            // Récupération de toutes les informations pour créer un objet EventObject 
            sql.Append("SELECT evt.EventId, contest.ContestId,  contest.ChampionshipId, contest.SeasonId, ");
            sql.Append("evt.DayNumber, ");
            sql.Append("evt.OpenDate, evt.CloseDate, season.Display as SeasonName ");
            sql.Append("FROM dbo.[Events] as evt ");
            sql.Append("LEFT JOIN dbo.Contests contest ON contest.ChampionshipId = @p1 ");
            sql.Append("LEFT JOIN dbo.Seasons season ON season.SeasonId = contest.SeasonId ");
            sql.Append("WHERE contest.SeasonId=@p2 ");
            sql.Append("ORDER BY evt.CloseDate");

            // Exécution de la requete SQL
            IEnumerable<EventObject> results = dbContext.ExecuteStoreQuery<EventObject>(sql.ToString(), parameters);
            // Constitution de la liste
            foreach (EventObject sqlObject in results)
            {
                returnList.Add(sqlObject);
            }

            return returnList;
        }
        

        /// <summary>
        /// Renvoie le meilleur score de l'événement pour la compétition personnalisée donnée
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="eventid">Identifiant unique de l'événement</param>
        /// <param name="customContestId">Identifiant unique de la compétition personnalisée</param>
        /// <returns>Meilleur score ou 0 si l'événement n'existe pas ou n'a pas encore eu lieu</returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static int GetBestScore(MyBetDBEntities dbContext, int eventid, int customContestId)
        {

            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }

            object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "eventId", Value = eventid },
                           new SqlParameter { ParameterName = "customContestId", Value = customContestId }
                        };

            StringBuilder sql = new StringBuilder();
            sql.Append("SELECT MAX(es.score) ");
            sql.Append("FROM dbo.EventScores es ");
            sql.Append("LEFT JOIN dbo.Subscriptions su on su.UserId=es.UserId ");
            sql.Append("WHERE es.EventId=@eventId and su.CustomContestId=@customContestId");
            // Get user information
            Int32? results = dbContext.ExecuteStoreQuery<Int32?>(sql.ToString(), parameters).FirstOrDefault();

            if (results.HasValue)
            {
                return results.Value;
            }
            return 0;

        }

        /// <summary>
        /// Mise à jour des résultats d'un événement pour un utilisateur donné
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="userId">GUID de l'utilisateur a mettre à jour</param>
        /// <param name="evt">Evenement concerné</param>
        /// <param name="rank">Informations de classement de l'utilisateur</param>
        /// <param name="isBestScore">0 si ce n'est pas le meilleur score de la journée, 1 sinon</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        /// <exception cref="ArgumentException">Evénement vaut null</exception>
        /// <exception cref="ArgumentException">Classement vaut null</exception>
        public static void AddOrUpdateEventScore(MyBetDBEntities dbContext, Guid userId, Events evt, RankObject rank, bool isBestScore, byte isEventCompleted, int customContestId)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }
            if (evt == null)
            {
                throw new ArgumentException("AddOrUpdateScore - event can not be null");
            }
            if (rank == null)
            {
                throw new ArgumentException("AddOrUpdateScore - rank can not be null");
            }

            byte isBestScoreByte = 0;
            if (isBestScore)
            {
                isBestScoreByte = 1; 
            }

            //EventScores scores = GetUserEventScoreObject(dbContext, userId, evt.EventId);
            //if (scores == null)
            //{
            //    // Aucune information pour cet utilisateur sur cet événement
            //    EventScores newScore = new EventScores();
            //    newScore.UserId = userId;
            //    newScore.EventId = evt.EventId;
            //    newScore.Score = System.Convert.ToInt32(rank.TotalScore);
            //    newScore.GoodPredictionCount = System.Convert.ToInt32(rank.GoodPredictionCount);
            //    newScore.GoodScoreCount = System.Convert.ToInt32(rank.GoodScoreCount);
            //    newScore.CurrentRank = rank.CurrentRank;
            //    newScore.IsBestScore = isBestScore;
            //    newScore.IsCompleted = isEventCompleted;
            //    dbContext.EventScores.AddObject(newScore);
            //}
            //else
            //{
            //    // Mise à jour des informations
            //    scores.Score = System.Convert.ToInt32(rank.TotalScore);
            //    scores.GoodPredictionCount = System.Convert.ToInt32(rank.GoodPredictionCount);
            //    scores.GoodScoreCount = System.Convert.ToInt32(rank.GoodScoreCount);
            //    scores.CurrentRank = rank.CurrentRank;
            //    scores.IsCompleted = isEventCompleted;
            //    scores.IsBestScore = isBestScore;
            //}

            int returnValue;
            object[] parameters = 
            {
                new SqlParameter { ParameterName = "p0", Value = userId },
                new SqlParameter { ParameterName = "p1", Value = evt.EventId },
                new SqlParameter { ParameterName = "p2", Value = System.Convert.ToInt32(rank.TotalScore) },
                new SqlParameter { ParameterName = "p3", Value = System.Convert.ToInt32(rank.GoodPredictionCount) },
                new SqlParameter { ParameterName = "p4", Value = System.Convert.ToInt32(rank.GoodScoreCount) },
                new SqlParameter { ParameterName = "p5", Value = rank.Rank },
                new SqlParameter { ParameterName = "p6", Value = isBestScoreByte },
                new SqlParameter { ParameterName = "p7", Value = isEventCompleted },
                new SqlParameter { ParameterName = "p8", Value = customContestId }
            };

             IEnumerable<int> queryResult = dbContext.ExecuteStoreQuery<int>
                    (String.Format(@"DECLARE @return_value int
                                     EXEC @return_value = [dbo].[mybet_updateScore]
                                     @UserId = @p0,
	                                 @EventId = @p1,
	                                 @Score = @p2,
	                                 @GoodPredictionCount = @p3,
	                                 @GoodScoreCount = @p4,
	                                 @CurrentRank = @p5,
	                                 @IsBestScore = @p6,
	                                 @IsCompleted = @p7,
	                                 @CustomContestId = @p8
                                     SELECT	@return_value"), parameters);

                    returnValue = queryResult.FirstOrDefault();
                    switch (returnValue)
                    {
                        case 0:
                            // Succes
                            return;
                        default:
                            // Erreur SQL interne
                            throw new System.Exception(String.Format("AddOrUpdateEventScore - Internal Error : {0}", returnValue));
                    }


        }

        ///// <summary>
        ///// Renvoie les résultats d'un événement pour un utilisateur donné 
        ///// </summary>
        ///// <param name="dbContext">Connexion à la base de données</param>
        ///// <param name="userId">GUID de l'utilisateur</param>
        ///// <param name="eventId">Identifiant unique de l'événement</param>
        ///// <returns></returns>
        ///// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        //public static EventScores GetUserEventScoreObject(MyBetDBEntities dbContext, Guid userId, int eventId)
        //{
        //    if (dbContext == null)
        //    {
        //        throw new ArgumentException("Database connection error");
        //    }
        //    var query = (from evtScore in dbContext.EventScores
        //                 where evtScore.EventId == eventId && evtScore.UserId == userId
        //                 select evtScore);
        //    EventScores currentScore = query.FirstOrDefault();
        //    return currentScore;
        //}

        /// <summary>
        /// Ajoute ou met à jour un nouvel événement au championnat
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="evt">Evenement à ajouter</param>
        /// <param name="contestId">Identifiant de la compétition (ex : Ligue 1 2011/2012)</param>
        /// <param name="dayNumber">Numéro de la journée</param>
        /// <param name="openDate">Date d'ouverture des paris de la journée</param>
        /// <param name="closeDate">Date de fermeture des paris de la journée</param>
        /// <exception cref="ArgumentException">Connexion à la base de données vaut null</exception>
        public static void AddOrUpdateEvent(MyBetDBEntities dbContext, Events evt, int contestId, int dayNumber, DateTime openDate, DateTime closeDate)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }

            if (evt == null)
            {
                // Mode "ajout"
                Events newEvent = new Events();
                newEvent.ContestId = contestId;
                newEvent.DayNumber = dayNumber;
                newEvent.OpenDate = openDate;
                newEvent.CloseDate = closeDate;
                dbContext.Events.AddObject(newEvent);
            }
            else
            {
                // Mode "mise à jour"
                evt.ContestId = contestId;
                evt.DayNumber = dayNumber;
                evt.OpenDate = openDate;
                evt.CloseDate = closeDate;
            }
            dbContext.SaveChanges();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbContext"></param>
        /// <param name="eventId"></param>
        /// <param name="openDate"></param>
        /// <param name="closeDate"></param>
        /// <returns></returns>
        public static bool SetEventDates(MyBetDBEntities dbContext, int eventId, DateTime openDate, DateTime closeDate)
        {

            if (dbContext == null)
            {
                throw new ArgumentException("Database connection error");
            }

            Events query = (from evt in dbContext.Events
                         where evt.EventId == eventId
                         select evt).FirstOrDefault();

            if (query == null)
            {
                // aucun événement avec cet identifiant
                return false;
            }
            // Modification des dates
            query.OpenDate = openDate;
            query.CloseDate = closeDate;
            // Sauvegarde des modifications si nécessaire
            dbContext.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);

            return true;
        }

        #endregion

    }
}